// $codepro.audit.disable spellCheckStringLiterals, sp, overloadedMethodsellCheckIdentifiers, spellCheckComments, spellCheckIdentifiers, numericLiterals, unusedMethod, nullPointerDereference
/**
 * Package domain
 */
package teamrocket.domain; // $codepro.audit.disable packagePrefixNamingConvention

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;

import teamrocket.db.DBWrapper;
import teamrocket.db.LoginAttempt;
import teamrocket.logging.TLogger;

/**
 * Represents a user in the database
 * 
 * @author Stephen
 * 
 * @version $Revision: 1.0 $
 */
public class User implements IDBObject<User> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	/**
	 * Field LOGIN.
	 * (value is ""l"")
	 */
	public static final String LOGIN = "l";
	
	/**
	 * Field REQAPPT.
	 * (value is ""a"")
	 */
	public static final String REQAPPT = "a";
	
	/**
	 * Field VIEWMHIST.
	 * (value is ""m"")
	 */
	public static final String VIEWMHIST = "m";
	
	/**
	 * Field MODPATIENTS.
	 * (value is ""P"")
	 */
	public static final String MODPATIENTS = "P";
	
	/**
	 * Field MODTR.
	 * (value is ""T"")
	 */
	public static final String MODTR = "T";
	
	/**
	 * Field VIEWAPPTS.
	 * (value is ""A"")
	 */
	public static final String VIEWAPPTS = "A";
	
	/**
	 * Field MODDRORDERS.
	 * (value is ""D"")
	 */
	public static final String MODDRORDERS = "D";
	
	/**
	 * Field VIEWREPORTS.
	 * (value is ""r"")
	 */
	public static final String VIEWREPORTS = "r";
	
	/**
	 * Field MODUSERS.
	 * (value is ""U"")
	 */
	public static final String MODUSERS = "U";
	
	/**
	 * Field username.
	 */
	private String username;
	
	/**
	 * Field password.
	 */
	private String password;
	
	/**
	 * Field firstName.
	 */
	protected String firstName;
	
	/**
	 * Field lastName.
	 */
	protected String lastName;
	
	/**
	 * Field accessString.
	 */
	private String accessString;
	
	/**
	 * Field usertype.
	 */
	private String usertype;
	
	/**
	 * Field uid.
	 */
	private int uid;
	
	/**
	 * Field isLoggedIn.
	 */
	private boolean isLoggedIn; // new--update the domain CRC

	/**
	 * Constructs a user in memory
	 * 
	 * @param username
	 * @param password
	 * @param firstName
	 * @param lastName
	 * @param usertype
	 * @param accessString
	 */
	public User(final String username, final String password, // $codepro.audit.disable largeNumberOfParameters
			final String firstName, final String lastName,
			final String usertype, final String accessString) {
		this.username = username;
		this.password = password;
		this.firstName = firstName;
		this.lastName = lastName;
		this.accessString = accessString;
		this.usertype = usertype;
		this.isLoggedIn = false;
	}

	/**
	 * Validates this user's password
	 * 
	 * @param pwd
	
	 * @return boolean
	 */
	public boolean validate(final String pwd) { // $codepro.audit.disable booleanMethodNamingConvention
		return password.equals(pwd);
	}

	/**
	 * Method setLoggedIn.
	 */
	public void setLoggedIn() { // $codepro.audit.disable accessorMethodNamingConvention
		isLoggedIn = true;
	}

	/**
	 * Method getUsertype.
	 * @return String
	 */
	public String getUsertype() {
		return usertype;
	}

	/**
	 * Method setUsertype.
	 * @param usertype String
	 */
	public void setUsertype(final String usertype) {
		this.usertype = usertype;
	}

	/**
	 * Method unsetLoggedIn.
	 */
	public void unsetLoggedIn() {
		isLoggedIn = false;
	}

	/**
	 * Method isLoggedIn.
	 * @return boolean
	 */
	public boolean isLoggedIn() {
		return isLoggedIn;
	}

	/**
	 * Method getUsername.
	 * @return String
	 */
	public String getUsername() {
		return username;
	}

	/**
	 * Method setUsername.
	 * @param username String
	 */
	public void setUsername(final String username) {
		this.username = username;
	}

	/**
	 * Method getPassword.
	 * @return String
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * Method setPassword.
	 * @param password String
	 */
	public void setPassword(final String password) {
		this.password = password;
	}

	/**
	 * Method getFirstName.
	 * @return String
	 */
	public String getFirstName() {
		return firstName;
	}

	/**
	 * Method setFirstName.
	 * @param firstName String
	 */
	public void setFirstName(final String firstName) {
		this.firstName = firstName;
	}

	/**
	 * Method getLastName.
	 * @return String
	 */
	public String getLastName() {
		return lastName;
	}

	/**
	 * Method setLastName.
	 * @param lastName String
	 */
	public void setLastName(final String lastName) {
		this.lastName = lastName;
	}

	/**
	 * Method setAccessString.
	 * @param accessString String
	 */
	public void setAccessString(final String accessString) {
		this.accessString = accessString;
	}

	/**
	 * Method getAccessString.
	 * @return String
	 */
	public String getAccessString() {
		return accessString;
	}

	/**
	 * Method getUID.
	 * @return int
	 */
	public int getUID() {
		return uid;
	}

	/**
	 * Method setUID.
	 * @param uid int
	 */
	public void setUID(final int uid) {
		this.uid = uid;
	}

	/**
	 * Method create.
	 * @return IDBObject<User>
	 * @see teamrocket.domain.IDBObject#create()
	 */
	@Override
	public IDBObject<User> create() {
		try {
			try {
				DBWrapper
						.execute(
								"INSERT INTO users (password, username, firstname, " +
								"lastname, usertype, accessstring) VALUES (?,?,?,?,?,?)",
								password, username, firstName, lastName,
								usertype, accessString);

				if (DBWrapper.DEBUG) {
					Thread.sleep(1000);
				}

				// Load the newly auto-generated UID into the uid field.
				final ResultSet result = DBWrapper.query(
						"SELECT id FROM users WHERE username=(?)",
						this.username);

				boolean empty = true;
				while (result.next()) {
					empty = false;
					this.uid = result.getInt("id");
				}
				if (empty) {
					throw new Exception("Unable to retrieve User ID.");
				}

			} catch (final SQLException e) {
				e.printStackTrace();
			}
		} catch (final Exception e) {
			e.printStackTrace();
		}
		TLogger.getInstance().getLogger().log(Level.INFO,
				"Creating User to Database...");
		return this;
	}

	/**
	 * Method update.
	 * @see teamrocket.domain.IDBObject#update()
	 */
	@Override
	public void update() {
		try {
			TLogger.getInstance().getLogger().log(
					Level.INFO,
					"Updating User in Database...");
			DBWrapper
					.execute(
							"UPDATE users SET password = ?, username = ?, firstname = ?,"
							+ " lastname = ?, usertype = ?, accessstring = ? WHERE id=?",
							password, username, firstName, lastName, usertype,
							accessString, uid);
		} catch (final SQLException e) {
			e.printStackTrace();
		}
		TLogger.getInstance().getLogger().log(
				Level.INFO,
				"Updating User in Database...");
	}

	/**
	 * Method destroy.
	 * @see teamrocket.domain.IDBObject#destroy()
	 */
	@Override
	public void destroy() {
		try {
			DBWrapper.execute("DELETE FROM users WHERE id=(?)", uid);
		} catch (final SQLException e) {
			e.printStackTrace();
		}
		TLogger.getInstance().getLogger().log(Level.INFO,
				"Deleting User in Database...");
	}

	/**
	 * Gets all users in the database
	 * 
	
	 * @return List<User>
	 */
	public static List<User> getUsers() {
		final List<User> users = new ArrayList<User>();
		try {
			final ResultSet rs = DBWrapper.query("SELECT * FROM users");
			while (rs.next()) {
				final User toAdd = new User(rs.getString("username"), rs
						.getString("password"), rs.getString("firstname"), rs
						.getString("lastname"), rs.getNString("usertype"), rs
						.getString("accessstring"));
				toAdd.setUID(rs.getInt("id"));
				users.add(toAdd);
			}
		} catch (final SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		TLogger.getInstance().getLogger().log(Level.INFO,
				"Getting Users in Database...");
		return users;
	}

	/**
	 * Method saveField.
	 * @param fieldName String
	 * @param value String
	 * @throws SQLException
	 */
	private void saveField(final String fieldName, final String value) // $codepro.audit.disable unusedMethod
			throws SQLException {
		DBWrapper.execute("update users where SET " + fieldName
				+ " = ? where id = ?", value, uid);
	}

	/**
	 * Method saveField.
	 * @param fieldname String
	 * @param value Integer
	 * @throws SQLException
	 */
	private void saveField(final String fieldname, final Integer value) // $codepro.audit.disable overloadedMethods
			throws SQLException {
		DBWrapper.execute("update users where SET, overloadedMethods " + fieldname
				+ " = ? where id = ?", value, uid);
	}

	/**
	 * Method saveField.
	 * @param fieldname String
	 * @param value Date
	 * @throws SQLException
	 */
	private void saveField(final String fieldname, final Date value) // $codepro.audit.disable overloadedMethods
			throws SQLException {
		DBWrapper.execute("update users where SET " + fieldname
				+ " = ? where id = ?", value, uid);
	}

	/**
	 * Constructs
	 * 
	 * @param username
	 * @return User
	 * @throws SQLException */
	public static User createFilledUser(final String username)
			throws SQLException {
		User outUser = null;
		final ResultSet rs = DBWrapper.query(
				"SELECT password, firstname, lastname, usertype, " +
				"accessstring, id FROM users where username = ?", username);
		if (rs.next()) {
			outUser = new User(username, rs.getString("password"), rs
					.getString("firstname"), rs.getString("lastname"), rs
					.getNString("usertype"), rs.getString("accessstring"));
			outUser.setUID(rs.getInt("id"));
		}
		return outUser;
	}

	/**
	 * Method createFilledUser.
	 * @param id int
	 * @return User
	 * @throws SQLException
	 */
	public static User createFilledUser(final int id) throws SQLException { //<!-- // $codepro.audit.disable overloadedMethods -->
		User outUser = null;
		final ResultSet rs = DBWrapper
				.query(
						"SELECT username, password, firstname, " +
						"lastname, usertype, accessstring, " +
						"id FROM users where id = ?", id);
		if (rs.next()) {
			outUser = new User(rs.getString("username"), rs
					.getString("password"), rs.getString("firstname"), rs
					.getString("lastname"), rs.getNString("usertype"), rs
					.getString("accessstring"));
			outUser.setUID(id);
		}
		return outUser;
	}

	/**
	 * Checks if this user can log in and creates a record of this user logging
	 * in
	 * 
	 * @param password String
	 * @return int
	 * @throws SQLException */
	public int doLogin(final String password) throws SQLException {

		boolean locked = false;
		// check for login permission
		if (accessString.contains(User.LOGIN)) {
			// check password
			if (password.equals(this.password)) {
				final LoginAttempt attempt = new LoginAttempt(this.uid, true,
						new Date());
				attempt.create();
				TLogger.getInstance().getLogger().log(Level.INFO,
						"User attempt to login is successful.");
				return 0;
			} else {
				TLogger.getInstance().getLogger().log(Level.INFO,
						"User attempt to login is not successful.");
				final LoginAttempt attempt = new LoginAttempt(this.uid, false,
						new Date());
				attempt.create();

			}
		} else {
			TLogger
					.getInstance()
					.getLogger()
					.log(Level.INFO, "User attempt to login " +
							"is not successful because they are locked out.");
			final LoginAttempt attempt = new LoginAttempt(this.uid, false,
					new Date());
			attempt.create();
			locked = true;

		}

		// check for 3 failed attempts
		if (accessString.contains(User.LOGIN)) {
			final ResultSet rs = DBWrapper
					.query(
							"select success, date from loginattempt " +
							"where userid = ? order by date",
							getUID());

			int numFails = 0;
			while (rs.next()) {
				final boolean loggedIn = rs.getBoolean("success");
				if (!loggedIn) {
					numFails++;
				} else {
					numFails = 0;
				}

			}
			if (numFails >= 3) {
				accessString = accessString.replaceAll(User.LOGIN, "");
				update();
				TLogger.getInstance().getLogger().log(Level.INFO,
						"Locking out user: ");
			}
		}

		if (locked) {
			return 2;
		} else {
			return 1;
		}
	}

	/**
	 * Method suspend.
	 */
	public void suspend() {
		accessString = accessString.replaceAll(LOGIN, "");
		update();
	}

	/**
	 * Method unsuspend.
	 */
	public void unsuspend() {
		if (!accessString.contains(LOGIN)) {
			this.accessString += LOGIN;
			update();
		}
	}

	/**
	 * Method lookupAllUsersOfType.
	 * @param userType String
	 * @return List<User>
	 */
	public static List<User> lookupAllUsersOfType(final String userType) {
		final List<User> foundUsers = new ArrayList<User>();

		final String sqlStatement = "SELECT * FROM users WHERE usertype = '"
				+ userType + "'";

		try {
			final ResultSet rs = DBWrapper.query(sqlStatement);

			while (rs.next()) {

				final User u = new User(rs.getString("username"), // $codepro.audit.disable questionableName
						rs.getString("password"), rs.getString("firstname"),
						rs.getString("lastname"), rs.getString("usertype"),
						rs.getString("accessstring"));
				u.setUID(rs.getInt("id"));
				foundUsers.add(u);

			}
		} catch (final SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return foundUsers;

	}

	/**
	 * Method lookupUser.
	 * @param userID int
	 * @return User
	 */
	public static User lookupUser(final int userID) {

		final String sqlStatement = "SELECT * FROM users WHERE id = ?";

		try {
			final ResultSet rs = DBWrapper.query(sqlStatement, userID);
			while (rs.next()) {
				final User u = new User(rs.getString("username"), // $codepro.audit.disable questionableName
						rs.getString("password"), rs.getString("firstname"),
						rs.getString("lastname"), rs.getString("usertype"),
						rs.getString("accessstring"));
				u.setUID(rs.getInt("id"));
				return u;
			}
		} catch (final SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Method tryGetPatientId.
	 * @return int
	 */
	public int tryGetPatientId() {
		int patientId = -1;
		try {
			final ResultSet rs = DBWrapper.query(
					"SELECT * FROM patient WHERE userid = ?", getUID());
			if (rs.next()) {
				patientId = rs.getInt("id");
			}

		} catch (final SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return patientId;

	}

	/**
	 * Method toString.
	 * @return String
	 */
	@Override
	public String toString() {
		return lastName + ", " + firstName;
	}
}
